from contextlib import asynccontextmanager
from fastapi import Body, FastAPI, File, UploadFile, HTTPException, status
import cv2
import numpy as np
from pydantic import BaseModel
import random
import threading
from frida.core import Device
import numpy as np
from lamda.client import *



class AnalysisResult(BaseModel):
    target_x_position:int

def get_target_hole_position(image_bytes) -> int | None:

    try:
        nppr = np.frombuffer(image_bytes,np.uint8)
        image_np= cv2.imdecode(nppr,cv2.IMREAD_COLOR)
        if image_np is None:
            print("你传入的图片数据为空")
            return None
    except Exception as e :
        print(f"传入的数据格式有问题，请确保传入的是图片字节流：{e}")
        return None 
    gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


    potential_holes_x = []
    for contour in contours:
        area = cv2.contourArea(contour)
        # print(f"面积:{area}")
        if 10000 < area < 20000:
            M = cv2.moments(contour)
            if M["m00"] != 0:
                cx = int(M["m10"] / M["m00"])
                potential_holes_x.append(cx)

    return max(potential_holes_x) if potential_holes_x else None

def saveImg(d):
    backgroundUI = d(resourceId = "captcha-verify-image")
    # output_filename = "backgroundImg.png"
    # backgroundimg = backgroundUI.screenshot(quality=100).save(output_filename)
    backgroundTyte = backgroundUI.screenshot(quality = 100).getvalue()
    return backgroundTyte

def move(d, sp):
    slideUI = d(className="android.widget.Image").get(1)
    sx = ((slideUI.get(1).info().bounds.left)+(slideUI.get(1).info().bounds.right)) // 2 
    # print("sx:",sx)
    sy = ((slideUI.get(1).info().bounds.top)+(slideUI.get(1).info().bounds.bottom)) // 2

    calibration_offset = 137  
    end_x = sp + calibration_offset
    
    # 计算需要滑动的总距离
    total_move_distance = end_x - sx

    print(f"滑块起始坐标: ({sx}, {sy})")
    print(f"识别出的缺口坐标: {sp}px")
    print(f"校准后的目标终点X坐标: {end_x}px")
    print(f"需要滑动的总距离: {total_move_distance}px")

    # 安全检查：如果计算出的距离过小或为负，说明可能出错了，避免无效操作
    if total_move_distance <= 10:
        print("警告: 计算出的滑动距离过小，操作可能无效或已在目标位置。结束滑动操作。")
        return None
    print("开始滑动")
    touch = TouchBuilder().down(x=sx, y=sy,pressure=random.randint(50,70)).wait(random.randint(150, 250))
    steps = random.randint(6, 9)
    for i in range(steps):
        progress = (i + 1) / steps
        ease_out_progress = 1 - (1 - progress) ** 3
        current_step_x = sx + total_move_distance * ease_out_progress
        random_y_offset = random.randint(-8, 8)
        wait_ms = random.randint(100, 400)
        touch.move(x=int(current_step_x), y=sy + random_y_offset,pressure=random.randint(50,70)).wait(wait_ms)
        
    # 模拟人为拖动超出距离再拖回来
    overshoot_x = end_x + random.randint(4, 10)
    touch.move(x=overshoot_x, y=sy + random.randint(-5, 5),pressure=random.randint(50,70)).wait(random.randint(700, 900))
    touch.move(x=end_x, y=sy + random.randint(-2, 2),pressure=random.randint(50,70)).wait(random.randint(500, 900))
    touch.up()
    d.perform_touch(touch.build())
    
    print("滑动操作执行完毕。")
    return True


def check_verifyCode(d):
    for j in range(3):
        captcha = d(resourceId="captcha-verify-image")
        print(f"开始尝试第{j}次")
        if captcha.exists():
            print("出现验证码,开始处理")
            load = d(resourceId = "com.zhiliaoapp.musically:id/h3f")
            stopThread.wait(1)
            for i in range(5):
                if  not load.exists():  
                    image_types = saveImg(d)
                    x = get_target_hole_position(image_types)
                    move(d,x)
                    return True
                else:  
                    stopThread.wait(10)
                    print(f"尝试等待第{i+1}次")     
            else:
                stopThread.wait(3)
                back = d(resourceId = "com.android.systemui:id/back")
                print("已经尝试5次，还是不能滑过，开始点击返回")
                back.click()
                return False
        else:
            break
    else:
        print(f"{d}设备正常")
        return

stopThread=threading.Event()
montior_thread = None

def check_verifyCode_thread():
    while not stopThread.is_set():
        ips = [
                "192.168.18.104",
                "192.168.18.105",
                "192.168.18.107",
                "192.168.18.109",
                "192.168.18.110",
                "192.168.18.111",
                "192.168.18.112",
                "192.168.18.113",
                "192.168.18.114",
                "192.168.18.115",
                "192.168.18.116",
                "192.168.18.117",
                "192.168.18.119",
                "192.168.18.120",
                "192.168.18.121",
                "192.168.18.122",
                "192.168.18.123",
                "192.168.18.124"
            ]
        for ip in ips:
            if stopThread.is_set():
                break
            try:
                d = Device(ip,65008)
                check_verifyCode(d)
            except Exception as e :
                print(f"[!]监控线程发生异常：{e}")
        stopThread.wait(30)
    print("监控线程已停止！")

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("FastAPI启动中")
    global montior_thread

    montior_thread = threading.Thread(target=check_verifyCode_thread)
    montior_thread.daemon= True
    montior_thread.start()
    print("监控线程已启动")

    yield

    print("FastAPI应用关闭中")
    stopThread.set()
    montior_thread.join(timeout=3)

app = FastAPI(
    lifespan=lifespan,
    title="滑块检测"
)

@app.post(
    "/api/getSpace",
    response_model=AnalysisResult,
    tags=["Image Analysis"]
)
async def analysis_iamge_from_bytes(
    image_bytes:bytes = Body(...,media_type="image/png"),description="要分析的图的原始字节流。"
):
    target_x = get_target_hole_position(image_bytes)
    if target_x is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="在图片中未找到符合条件的目标缺口。"
        )
    return AnalysisResult(target_x_position=target_x)






